Skip to content

feat add codex fast mode support#2

Open
skyguan92 wants to merge 2 commits intosplit/provider-model-pickerfrom
split/codex-fast-mode
Open

feat add codex fast mode support#2
skyguan92 wants to merge 2 commits intosplit/provider-model-pickerfrom
split/codex-fast-mode

Conversation

@skyguan92
Copy link
Copy Markdown
Owner

Summary

  • add provider-aware Codex fast mode support on top of the new picker flow
  • surface Codex-specific model/effort/fast-mode display in startup and status UI
  • persist provider-target fast mode data and pass Codex service tier through request resolution

Notes

  • stacked on split/provider-model-picker
  • after the picker PR merges, this branch should be rebased and retargeted upstream

Testing

  • env -i PATH=/Users/jguan/.bun/bin:/usr/bin:/bin HOME=$HOME TMPDIR=${TMPDIR:-/tmp} bun test ./src/services/api/codexShim.test.ts ./src/utils/model/codexDisplay.test.ts ./src/utils/model/providerModelSettings.test.ts ./src/commands/model/model.test.tsx
  • env -i PATH=/Users/jguan/.bun/bin:/usr/bin:/bin HOME=$HOME TMPDIR=${TMPDIR:-/tmp} bun run build

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the fast mode functionality to support multiple providers, primarily integrating Codex and introducing a serviceTier setting. The changes include the addition of provider-aware fast mode utilities, updates to the API client and shims, and UI adjustments across various components. Feedback identifies a race condition in the applyFastMode function where the modelUpdated flag is returned before the state update completes, redundant state management logic, and a security regression caused by the removal of sensitive value redaction in the status display.

Comment on lines +54 to 103
function applyFastMode(options: {
enable: boolean
provider: FastModeCommandProvider
targetKey: string
setAppState: (f: (prev: AppState) => AppState) => void
}): { modelUpdated: boolean } {
const { enable, provider, targetKey, setAppState } = options

if (provider === 'codex') {
setCodexFastModeSelection(targetKey, enable ? 'fast' : null)
setAppState(prev => ({
...prev,
fastMode: enable,
}))
return { modelUpdated: false }
}

if (provider !== 'firstParty') {
return { modelUpdated: false }
}

clearFastModeCooldown()
updateSettingsForSource('userSettings', {
fastMode: enable ? true : undefined
});
fastMode: enable ? true : undefined,
})

if (enable) {
let modelUpdated = false
setAppState(prev => {
// Only switch model if current model doesn't support fast mode
const needsModelSwitch = !isFastModeSupportedByModel(prev.mainLoopModel);
modelUpdated = !isFastModeSupportedByModel(prev.mainLoopModel)
return {
...prev,
...(needsModelSwitch ? {
mainLoopModel: getFastModeModel(),
mainLoopModelForSession: null
} : {}),
fastMode: true
};
});
} else {
setAppState(prev => ({
...prev,
fastMode: false
}));
...(modelUpdated
? {
mainLoopModel: getFastModeModel(),
mainLoopModelForSession: null,
}
: {}),
fastMode: true,
}
})
return { modelUpdated }
}

setAppState(prev => ({
...prev,
fastMode: false,
}))
return { modelUpdated: false }
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The modelUpdated flag is being returned before the setAppState callback has executed. Since setAppState is asynchronous in React, modelUpdated will always return its initial value (false), which means the confirmation message won't correctly reflect model switches when they occur.

Additionally, the function should determine if a model switch is needed based on the current model passed as an argument to ensure the result is available synchronously.

function applyFastMode(options: {
  enable: boolean
  provider: FastModeCommandProvider
  targetKey: string
  setAppState: (f: (prev: AppState) => AppState) => void
  currentModel: string | null
}): { modelUpdated: boolean } {
  const { enable, provider, targetKey, setAppState, currentModel } = options

  if (provider === 'codex') {
    setCodexFastModeSelection(targetKey, enable ? 'fast' : null)
    setAppState(prev => ({
      ...prev,
      fastMode: enable,
    }))
    return { modelUpdated: false }
  }

  if (provider !== 'firstParty') {
    return { modelUpdated: false }
  }

  clearFastModeCooldown()
  updateSettingsForSource('userSettings', {
    fastMode: enable ? true : undefined,
  })

  if (enable) {
    const modelUpdated = !isFastModeSupportedByModel(currentModel)
    setAppState(prev => ({
      ...prev,
      ...(modelUpdated
        ? {
            mainLoopModel: getFastModeModel(),
            mainLoopModelForSession: null,
          }
        : {}),
      fastMode: true,
    }))
    return { modelUpdated }
  }

  setAppState(prev => ({
    ...prev,
    fastMode: false,
  }))
  return { modelUpdated: false }
}

Comment on lines +165 to +170
const { modelUpdated } = applyFastMode({
enable: enableFastMode,
provider,
targetKey: providerSelectionTargetKey,
setAppState,
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Update the applyFastMode call to pass the current model to support the signature change and fix the asynchronous flag bug.

Suggested change
const { modelUpdated } = applyFastMode({
enable: enableFastMode,
provider,
targetKey: providerSelectionTargetKey,
setAppState,
})
const { modelUpdated } = applyFastMode({
enable: enableFastMode,
provider,
targetKey: providerSelectionTargetKey,
setAppState,
currentModel: model,
})

Comment on lines +208 to +213
applyFastMode({
enable: false,
provider,
targetKey: providerSelectionTargetKey,
setAppState,
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Update the applyFastMode call to match the new signature.

        applyFastMode({
          enable: false,
          provider,
          targetKey: providerSelectionTargetKey,
          setAppState,
          currentModel: null,
        })

Comment on lines +333 to +338
const { modelUpdated } = applyFastMode({
enable: options.enable,
provider: options.provider,
targetKey: appState.providerSelectionTargetKey,
setAppState: options.setAppState,
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Update the applyFastMode call to pass the current model from appState.

  const appState = options.getAppState()
  const { modelUpdated } = applyFastMode({
    enable: options.enable,
    provider: options.provider,
    targetKey: appState.providerSelectionTargetKey,
    setAppState: options.setAppState,
    currentModel: appState.mainLoopModel,
  })

Comment thread src/commands/fast/fast.tsx Outdated
Comment on lines +177 to +186
if (
provider === 'firstParty' &&
!enableFastMode &&
!isFastModeSupportedByModel(model)
) {
setAppState((prev: AppState) => ({
...prev,
fastMode: false,
}))
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This block is redundant. applyFastMode (called on line 165) already handles setting fastMode: false in the app state when disabling fast mode for the firstParty provider.

Comment thread src/utils/status.tsx Outdated
properties.push({
label: 'Model',
value: redactSecretValueForDisplay(modelDisplay, process.env) ?? modelDisplay
value: modelDisplay
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The removal of redactSecretValueForDisplay is a potential security regression. This utility prevents sensitive information (like API keys) from being surfaced in the UI if they are accidentally used as model names in environment variables. It should be retained.

Suggested change
value: modelDisplay
value: redactSecretValueForDisplay(modelDisplay, process.env) ?? modelDisplay

Comment thread src/utils/status.tsx Outdated
properties.push({
label: 'Model',
value: redactSecretValueForDisplay(modelDisplay, process.env) ?? modelDisplay
value: modelDisplay
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The removal of redactSecretValueForDisplay is a potential security regression. This utility prevents sensitive information (like API keys) from being surfaced in the UI if they are accidentally used as model names in environment variables. It should be retained.

Suggested change
value: modelDisplay
value: redactSecretValueForDisplay(modelDisplay, process.env) ?? modelDisplay

@skyguan92
Copy link
Copy Markdown
Owner Author

Pushed a follow-up in 830de27 for the valid review items. I restored redactSecretValueForDisplay(...) on the status model display and removed the redundant fastMode: false state update in the picker. Verified against the related existing tests with bun test src/utils/providerProfile.test.ts src/utils/model/codexDisplay.test.ts src/utils/effort.provider.test.ts.

I did not thread a separate currentModel argument through applyFastMode: the app store setState here is synchronous, so modelUpdated is computed synchronously from the updater callback rather than via React async state.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant